package com.luych.toolbox.gateway.center.websocket;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.luych.toolbox.common.feign.param.gateway.center.WebSocketChat;
import com.luych.toolbox.common.feign.param.gateway.center.WebSocketEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.List;
import java.util.Map;

@Component
@ServerEndpoint("/api/websocket")
public class WebSocketEndPoint {

    private static ObjectMapper objectMapper;
    private static final Logger logger = LoggerFactory.getLogger(WebSocketEndPoint.class);

    @Autowired
    public void setObjectMapper(ObjectMapper objectMapper) {
        WebSocketEndPoint.objectMapper = objectMapper;
    }

    public void onSend(String userId, WebSocketMessage message) throws IOException {
        this.onSend(userId, objectMapper.writeValueAsString(message));
    }

    public void onSend(String userId, String message) throws IOException {
        Session session = WebSocketCache.get(userId);
        if (session == null) {
            logger.info("Can't find websocket session with userId: {}", userId);
            return;
        }
        session.getBasicRemote().sendText(message);
        logger.info("Websocket send message, userId: {}, message: {}", userId, message);
    }

    @OnOpen
    public void onOpen(Session session) throws IOException {
        Map<String, List<String>> params = session.getRequestParameterMap();
        List<String> userIds = params.get("userId");
        if (userIds == null || userIds.isEmpty()) {
            logger.warn("Websocket connected but no userId, close it");
            session.close();
            return;
        }
        String userId = userIds.get(0);

        for (Map.Entry<String, Session> sessionEntry : WebSocketCache.getSessions().entrySet()) {
            WebSocketEvent webSocketEvent = new WebSocketEvent(WebSocketEvent.Type.ONLINE, userId);
            WebSocketMessage webSocketMessage = new WebSocketMessage(webSocketEvent);
            this.onSend(sessionEntry.getKey(), webSocketMessage);
        }

        WebSocketCache.set(userId, session);
        logger.info("Websocket connected, userId: {}", userId);

    }

    @OnClose
    public void onClose(Session session, CloseReason reason) throws IOException {
        String userId = WebSocketCache.getUserIdBySessionId(session.getId());
        WebSocketCache.del(userId);

        for (Map.Entry<String, Session> sessionEntry : WebSocketCache.getSessions().entrySet()) {
            WebSocketEvent webSocketEvent = new WebSocketEvent(WebSocketEvent.Type.OFFLINE, userId);
            WebSocketMessage webSocketMessage = new WebSocketMessage(webSocketEvent);
            this.onSend(sessionEntry.getKey(), webSocketMessage);
        }

        logger.info("Websocket disconnect, userId: {}, reason: {}", userId, reason.getReasonPhrase());
    }

    @OnMessage
    public String onMessage(String message, Session session) throws IOException {
        String userId = WebSocketCache.getUserIdBySessionId(session.getId());
        logger.info("Websocket receive message, userId: {}, message: {}", userId, message);
        JsonNode messageNode = objectMapper.readTree(message);
        String type = messageNode.get("type").asText();
        if (WebSocketMessage.Type.CHAT.name().equals(type)) {
            String chatUserId = messageNode.get("message").get("userId").asText();
            String chatMessage = messageNode.get("message").get("message").asText();

            Session chatSession = WebSocketCache.get(userId);
            if (chatSession == null) {
                WebSocketEvent webSocketEvent = new WebSocketEvent(WebSocketEvent.Type.OFFLINE, chatUserId);
                return objectMapper.writeValueAsString(new WebSocketMessage(webSocketEvent));
            } else {
                WebSocketChat webSocketChat = new WebSocketChat(userId, chatMessage);
                this.onSend(chatUserId, new WebSocketMessage(webSocketChat));

                WebSocketEvent webSocketEvent = new WebSocketEvent(WebSocketEvent.Type.SENDED, chatUserId);
                return objectMapper.writeValueAsString(new WebSocketMessage(webSocketEvent));
            }
        } else if (WebSocketMessage.Type.EVENT.name().equals(type)) {
            String eventType = messageNode.get("message").get("type").asText();
            if (WebSocketEvent.Type.HEARTBEAT.name().equals(eventType)) {
                return message;
            }
        }
        return null;
    }

    @OnError
    public void error(Session session, Throwable e) {
        String userId = WebSocketCache.getUserIdBySessionId(session.getId());
        logger.error("Websocket error, userId {}, error: {}", userId, e.getMessage(), e);
    }
}
